#ifndef GTL_RANDOM_HEADER_GUARD
#define GTL_RANDOM_HEADER_GUARD

#include "gtlCompilerSpecific.h"
#include <ctime>

namespace GTL
{
  namespace Random
  {
    ///************************************************************************/
    ///* All of these have a similiar interface:                              */
    ///*                                                                      */
    ///* GetRandomInteger(start = 0, end = int_max)                           */
    ///* GetRandomUnsignedInteger(start = 0, end = uint_max)                  */
    ///* GetRandomFloat(start = 0.0f, end = 1.0f)                             */
    ///*                                                                      */
    ///* Also, all of them are castable to uint, int and float, and the ()    */
    ///* operator returns a random uint                                       */
    ///*                                                                      */
    ///* They have default constructors, a copy constructor, a seed           */
    ///* constructor, and an assignment operator                              */
    ///************************************************************************/

    struct SimpleRandom; /// an extremely simple rand()-like impl
    struct SlowGoodRandom; /// mersenne twister
    struct FastGoodRandom; /// Marsaglia's MWC

    ///************************************************************************/
    ///* Implementation                                                       */
    ///************************************************************************/

    struct SimpleRandom
    {
      int GetRandomInteger(int start = 0, int end = INT_MAX)
      {
        unsigned int n = NextRand();
        return start + n%(end-start);
      }

      int GetRandomUnsignedInteger(unsigned int start = 0, unsigned int end = UINT_MAX)
      {
        unsigned int n = NextRand();
        return start + n%(end-start);
      }

      float GetRandomFloat(float start = 0, float end = 1.0f)
      {
        unsigned int n = NextRand();
        float f = float(n)/float(UINT_MAX);
        return start + f*(end-start);
      }

      void SetSeed(unsigned int seed) { mSeed = seed; }
      unsigned int GetCurrentSeed() const { return mSeed; }

      unsigned int operator()() { return GetRandomUnsignedInteger(); }
      operator unsigned int() { return GetRandomUnsignedInteger(); }
      operator int() { return GetRandomInteger(); }
      operator float() { return GetRandomFloat(); }

    public: /// constructors and destructors
      SimpleRandom() { SetSeed(clock()); }
      SimpleRandom(const SimpleRandom& other) : mSeed(other.mSeed) {}
      SimpleRandom& operator=(const SimpleRandom& other) { mSeed = other.mSeed; return *this; }
      SimpleRandom& operator=(unsigned int seed) { mSeed = seed; return *this; }
    private:
      unsigned int NextRand()
      {
        return mSeed = (((mSeed * 214013L + 2531011L) >> 16) & 0xffff) |
          ((mSeed * 1103515245L + 12345L) & 0xffff0000);
      }
      unsigned int mSeed;
    };

    /// based on the wiki article
    struct SlowGoodRandom
    {
      int GetRandomInteger(int start = 0, int end = INT_MAX)
      {
        unsigned int n = NextRand();
        return start + n%(end-start);
      }

      int GetRandomUnsignedInteger(unsigned int start = 0, unsigned int end = UINT_MAX)
      {
        unsigned int n = NextRand();
        return start + n%(end-start);
      }

      float GetRandomFloat(float start = 0, float end = 1.0f)
      {
        unsigned int n = NextRand();
        float f = float(n)/float(UINT_MAX);
        return start + f*(end-start);
      }

      unsigned int operator()() { return GetRandomUnsignedInteger(); }
      operator unsigned int() { return GetRandomUnsignedInteger(); }
      operator int() { return GetRandomInteger(); }
      operator float() { return GetRandomFloat(); }

    public: /// constructors and destructors
      SlowGoodRandom() : mTable(new int[624]), mIndex(0) { InitializeGenerator((unsigned int)time(NULL)); }
      SlowGoodRandom(unsigned int seed) : mTable(new int[624]), mIndex(0) { InitializeGenerator(seed); }
      SlowGoodRandom(const SlowGoodRandom& other)
        : mTable(new int[624]), mIndex(other.mIndex)
      {
        memcpy(mTable, other.mTable, sizeof(int)*624);
      }
      SlowGoodRandom& operator=(const SlowGoodRandom& other)
      {
        if (this == &other) return *this;

        mTable = new int[624];
        mIndex = other.mIndex;
        memcpy(mTable, other.mTable, sizeof(int)*624);
        return *this;
      }
      virtual ~SlowGoodRandom() { delete[] mTable; }
    private:
      unsigned int NextRand()
      {
        if (!mIndex) PopulateTable();

        unsigned int y = mTable[mIndex];
        y ^= (y >> 11);
        y ^= (y << 7) & 2636928640;
        y ^= (y << 15) & 4022730752;
        y ^= (y >> 18);

        mIndex = (mIndex+1)%624;

        return y;
      }

      void InitializeGenerator( unsigned int seed ) 
      {
        mTable[0] = seed;
        for (uint64_t i = 1; i<624; i++)
        {
          mTable[i] = (int)(((uint64_t)1812433253L * (uint64_t)(mTable[i-1] ^ (mTable[i-1] >> 30)) + i) & 0xFFFFFFFF);
        }
      }

      void PopulateTable() 
      {
        for (unsigned int i=0; i<624; i++)
        {
          unsigned int y = (mTable[i]&0x80000000) + ((mTable[(i+1) % 624]) & 0x7FFFFFFF);
          mTable[i] = mTable[(i + 397) % 624] ^ (y >> 1);
          if (y&1)
            mTable[i] ^= 2567483615;
        }
      }

      int* mTable;
      int mIndex;
    };

    /// Marsaglia's MWC, Agner Fog's impl, GPLed
    struct FastGoodRandom
    {
      int GetRandomInteger(int start = 0, int end = INT_MAX)
      {
        unsigned int n = NextRand();
        return start + n%(end-start);
      }

      int GetRandomUnsignedInteger(unsigned int start = 0, unsigned int end = UINT_MAX)
      {
        unsigned int n = NextRand();
        return start + n%(end-start);
      }

      float GetRandomFloat(float start = 0, float end = 1.0f)
      {
        unsigned int n = NextRand();
        float f = float(n)/float(UINT_MAX);
        return start + f*(end-start);
      }

      unsigned int operator()() { return GetRandomUnsignedInteger(); }
      operator unsigned int() { return GetRandomUnsignedInteger(); }
      operator int() { return GetRandomInteger(); }
      operator float() { return GetRandomFloat(); }

    public: /// constructors and destructors
      FastGoodRandom() { SetSeed((unsigned int)time(NULL)); }
      FastGoodRandom(unsigned int seed) { SetSeed(seed); }
      FastGoodRandom(const FastGoodRandom& other) { *this = other; }
      /*
      FastGoodRandom& operator=(const FastGoodRandom& other)
      {
        memcpy(mHistory, other.mHistory, sizeof(uint32_t)*5);
        return *this;
      }*/
    private:
      unsigned int NextRand()
      {
        uint64_t sum;
        sum = (uint64_t)2111111111UL * (uint64_t)mHistory[3] +
          (uint64_t)1492 * (uint64_t)(mHistory[2]) +
          (uint64_t)1776 * (uint64_t)(mHistory[1]) +
          (uint64_t)5115 * (uint64_t)(mHistory[0]) +
          (uint64_t)mHistory[4];
        mHistory[3] = mHistory[2];  mHistory[2] = mHistory[1];  mHistory[1] = mHistory[0];
        mHistory[4] = (uint32_t)(sum >> 32);                  // Carry
        mHistory[0] = (uint32_t)sum;                          // Low 32 bits of sum
        return mHistory[0];
      }

      void SetSeed( unsigned int seed ) 
      {
        uint32_t s = seed;
        for (int i = 0; i < 5; i++) {
          s = s * 29943829 - 1;
          mHistory[i] = s;
        }
        for (int i=0; i<19; i++) NextRand();
      }

      uint32_t mHistory[5];
    };
  }
}

#endif